//
// Mono.CSharp CSharpCodeProvider Class implementation
//
// Author:
//   Daniel Stodden (stodden@in.tum.de)
//   Marek Safar (marek.safar@seznam.cz)
//
// (C) 2002 Ximian, Inc.
//

//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

namespace Mono.CSharp
{
	using System;
	using System.CodeDom;
	using System.CodeDom.Compiler;
	using System.Globalization;
	using System.IO;
	using System.Reflection;
	using System.Collections;
	using System.Text;
	using System.Collections.Generic;
	
	internal class CSharpCodeGenerator
		: CodeGenerator
	{
		IDictionary <string, string> providerOptions;
		
		// It is used for beautiful "for" syntax
		bool dont_write_semicolon;

		//
		// Constructors
		//
		public CSharpCodeGenerator()
		{
			dont_write_semicolon = false;
		}

		public CSharpCodeGenerator (IDictionary <string, string> providerOptions)
		{
			this.providerOptions = providerOptions;
		}

		protected IDictionary <string, string> ProviderOptions {
			get { return providerOptions; }
		}
		
		//
		// Properties
		//
		protected override string NullToken {
			get {
				return "null";
			}
		}

		//
		// Methods
		//

		protected override void GenerateArrayCreateExpression (CodeArrayCreateExpression expression)
		{
			//
			// This tries to replicate MS behavior as good as
			// possible.
			//
			// The Code-Array stuff in ms.net seems to be broken
			// anyways, or I'm too stupid to understand it.
			//
			// I'm sick of it. If you try to develop array
			// creations, test them on windows. If it works there
			// but not in mono, drop me a note.  I'd be especially
			// interested in jagged-multidimensional combinations
			// with proper initialization :}
			//

			TextWriter output = Output;

			output.Write ("new ");

			CodeExpressionCollection initializers = expression.Initializers;
			CodeTypeReference createType = expression.CreateType;

			if (initializers.Count > 0) {

				OutputType (createType);

				if (expression.CreateType.ArrayRank == 0) {
					output.Write ("[]");
				}

				OutputStartBrace ();
				++Indent;
				OutputExpressionList (initializers, true);
				--Indent;
				output.Write ("}");
			} else {
				CodeTypeReference arrayType = createType.ArrayElementType;
				while (arrayType != null) {
					createType = arrayType;
					arrayType = arrayType.ArrayElementType;
				}

				OutputType (createType);

				output.Write ('[');

				CodeExpression size = expression.SizeExpression;
				if (size != null)
					GenerateExpression (size);
				else
					output.Write (expression.Size);

				output.Write(']');
			}
		}
		
		protected override void GenerateBaseReferenceExpression (CodeBaseReferenceExpression expression)
		{
			Output.Write ("base");
		}
		
		protected override void GenerateCastExpression (CodeCastExpression expression)
		{
			TextWriter output = Output;
			output.Write ("((");
			OutputType (expression.TargetType);
			output.Write (")(");
			GenerateExpression (expression.Expression);
			output.Write ("))");
		}


		protected override void GenerateCompileUnitStart (CodeCompileUnit compileUnit)
		{
			GenerateComment (new CodeComment ("------------------------------------------------------------------------------"));
			GenerateComment (new CodeComment (" <autogenerated>"));
			GenerateComment (new CodeComment ("     This code was generated by a tool."));
			GenerateComment (new CodeComment ("     Mono Runtime Version: " +  System.Environment.Version));
			GenerateComment (new CodeComment (""));
			GenerateComment (new CodeComment ("     Changes to this file may cause incorrect behavior and will be lost if "));
			GenerateComment (new CodeComment ("     the code is regenerated."));
			GenerateComment (new CodeComment (" </autogenerated>"));
			GenerateComment (new CodeComment ("------------------------------------------------------------------------------"));
			Output.WriteLine ();
			base.GenerateCompileUnitStart (compileUnit);
		}

		protected override void GenerateCompileUnit (CodeCompileUnit compileUnit)
		{
			GenerateCompileUnitStart (compileUnit);

			if (compileUnit.AssemblyCustomAttributes.Count > 0) {
				OutputAttributes (compileUnit.AssemblyCustomAttributes, 
					"assembly: ", false);
				Output.WriteLine ("");
			}

			foreach (CodeNamespace ns in compileUnit.Namespaces)
				GenerateNamespace (ns);

			GenerateCompileUnitEnd (compileUnit);
		}

#if NET_2_0
		protected override void GenerateDefaultValueExpression (CodeDefaultValueExpression e)
		{
			Output.Write ("default(");
			OutputType (e.Type);
			Output.Write (')');
		}
#endif

		protected override void GenerateDelegateCreateExpression (CodeDelegateCreateExpression expression)
		{
			TextWriter output = Output;

			output.Write ("new ");
			OutputType (expression.DelegateType);
			output.Write ('(');

			CodeExpression targetObject = expression.TargetObject;
			if (targetObject != null) {
				GenerateExpression (targetObject);
				Output.Write ('.');
			}
			output.Write (GetSafeName (expression.MethodName));

			output.Write (')');
		}

		protected override void GenerateFieldReferenceExpression (CodeFieldReferenceExpression expression)
		{
			CodeExpression targetObject = expression.TargetObject;
			if (targetObject != null) {
				GenerateExpression (targetObject);
				Output.Write ('.');
			}
			Output.Write (GetSafeName (expression.FieldName));
		}
		
		protected override void GenerateArgumentReferenceExpression (CodeArgumentReferenceExpression expression)
		{
			Output.Write (GetSafeName (expression.ParameterName));
		}

		protected override void GenerateVariableReferenceExpression (CodeVariableReferenceExpression expression)
		{
			Output.Write (GetSafeName (expression.VariableName));
		}
			
		protected override void GenerateIndexerExpression (CodeIndexerExpression expression)
		{
			TextWriter output = Output;

			GenerateExpression (expression.TargetObject);
			output.Write ('[');
			OutputExpressionList (expression.Indices);
			output.Write (']');
		}
		
		protected override void GenerateArrayIndexerExpression (CodeArrayIndexerExpression expression)
		{
			TextWriter output = Output;

			GenerateExpression (expression.TargetObject);
			output.Write ('[');
			OutputExpressionList (expression.Indices);
			output.Write (']');
		}
		
		protected override void GenerateSnippetExpression (CodeSnippetExpression expression)
		{
			Output.Write (expression.Value);
		}
		
		protected override void GenerateMethodInvokeExpression (CodeMethodInvokeExpression expression)
		{
			TextWriter output = Output;

			GenerateMethodReferenceExpression (expression.Method);

			output.Write ('(');
			OutputExpressionList (expression.Parameters);
			output.Write (')');
		}

		protected override void GenerateMethodReferenceExpression (CodeMethodReferenceExpression expression)
		{
			if (expression.TargetObject != null)
			{
				GenerateExpression (expression.TargetObject);
				Output.Write ('.');
			};
			Output.Write (GetSafeName (expression.MethodName));
			if (expression.TypeArguments.Count > 0)
				Output.Write (GetTypeArguments (expression.TypeArguments));
		}

		protected override void GenerateEventReferenceExpression (CodeEventReferenceExpression expression)
		{
			if (expression.TargetObject != null) {
				GenerateExpression (expression.TargetObject);
				Output.Write ('.');
			}
			Output.Write (GetSafeName (expression.EventName));
		}

		protected override void GenerateDelegateInvokeExpression (CodeDelegateInvokeExpression expression)
		{
			if (expression.TargetObject != null)
				GenerateExpression (expression.TargetObject);
			Output.Write ('(');
			OutputExpressionList (expression.Parameters);
			Output.Write (')');
		}
		
		protected override void GenerateObjectCreateExpression (CodeObjectCreateExpression expression)
		{
			Output.Write ("new ");
			OutputType (expression.CreateType);
			Output.Write ('(');
			OutputExpressionList (expression.Parameters);
			Output.Write (')');
		}

		protected override void GeneratePropertyReferenceExpression (CodePropertyReferenceExpression expression)
		{
			CodeExpression targetObject = expression.TargetObject;
			if (targetObject != null) {
				GenerateExpression (targetObject);
				Output.Write ('.');
			}
			Output.Write (GetSafeName (expression.PropertyName ));
		}

		protected override void GeneratePropertySetValueReferenceExpression (CodePropertySetValueReferenceExpression expression)
		{
			Output.Write ("value");
		}

		protected override void GenerateThisReferenceExpression (CodeThisReferenceExpression expression)
		{
			Output.Write ("this");
		}

		protected override void GenerateExpressionStatement (CodeExpressionStatement statement)
		{
			GenerateExpression (statement.Expression);
			if (dont_write_semicolon)
				return;
			Output.WriteLine(';');
		}

		protected override void GenerateIterationStatement (CodeIterationStatement statement)
		{
			TextWriter output = Output;

			dont_write_semicolon = true;
			output.Write ("for (");
			GenerateStatement (statement.InitStatement);
			output.Write ("; ");
			GenerateExpression (statement.TestExpression);
			output.Write ("; ");
			GenerateStatement (statement.IncrementStatement);
			output.Write (")");
			dont_write_semicolon = false;
			OutputStartBrace ();
			++Indent;
			GenerateStatements (statement.Statements);
			--Indent;
			output.WriteLine ('}');
		}

		protected override void GenerateThrowExceptionStatement (CodeThrowExceptionStatement statement)
		{
			Output.Write ("throw");
			if (statement.ToThrow != null) {
				Output.Write (' ');
				GenerateExpression (statement.ToThrow);
			}
			Output.WriteLine (";");
		}

		protected override void GenerateComment (CodeComment comment)
		{
			TextWriter output = Output;

			string commentChars = null;

			if (comment.DocComment) {
				commentChars = "///";
			} else {
				commentChars = "//";
			}

			output.Write (commentChars);
			output.Write (' ');
			string text = comment.Text;

			for (int i = 0; i < text.Length; i++) {
				output.Write (text[i]);
				if (text[i] == '\r') {
					if (i < (text.Length - 1) && text[i + 1] == '\n') {
						continue;
					}
					output.Write (commentChars);
				} else if (text[i] == '\n') {
					output.Write (commentChars);
				}
			}

			output.WriteLine ();
		}

		protected override void GenerateMethodReturnStatement (CodeMethodReturnStatement statement)
		{
			TextWriter output = Output;

			if (statement.Expression != null) {
				output.Write ("return ");
				GenerateExpression (statement.Expression);
				output.WriteLine (";");
			} else {
				output.WriteLine ("return;");
			}
		}

		protected override void GenerateConditionStatement (CodeConditionStatement statement)
		{
			TextWriter output = Output;
			output.Write ("if (");
			GenerateExpression (statement.Condition);
			output.Write (")");
			OutputStartBrace ();

			++Indent;
			GenerateStatements (statement.TrueStatements);
			--Indent;

			CodeStatementCollection falses = statement.FalseStatements;
			if (falses.Count > 0) {
				output.Write ('}');
				if (Options.ElseOnClosing)
					output.Write (' ');
				else
					output.WriteLine ();
				output.Write ("else");
				OutputStartBrace ();
				++Indent;
				GenerateStatements (falses);
				--Indent;
			}
			output.WriteLine ('}');
		}

		protected override void GenerateTryCatchFinallyStatement (CodeTryCatchFinallyStatement statement)
		{
			TextWriter output = Output;
			CodeGeneratorOptions options = Options;

			output.Write ("try");
			OutputStartBrace ();
			++Indent;
			GenerateStatements (statement.TryStatements);
			--Indent;
			
			foreach (CodeCatchClause clause in statement.CatchClauses) {
				output.Write ('}');
				if (options.ElseOnClosing)
					output.Write (' ');
				else
					output.WriteLine ();
				output.Write ("catch (");
				OutputTypeNamePair (clause.CatchExceptionType, GetSafeName(clause.LocalName));
				output.Write (")");
				OutputStartBrace ();
				++Indent;
				GenerateStatements (clause.Statements);
				--Indent;
			}

			CodeStatementCollection finallies = statement.FinallyStatements;
			if (finallies.Count > 0) {
				output.Write ('}');
				if (options.ElseOnClosing)
					output.Write (' ');
				else
					output.WriteLine ();
				output.Write ("finally");
				OutputStartBrace ();
				++Indent;
				GenerateStatements (finallies);
				--Indent;
			}

			output.WriteLine('}');
		}

		protected override void GenerateAssignStatement (CodeAssignStatement statement)
		{
			TextWriter output = Output;
			GenerateExpression (statement.Left);
			output.Write (" = ");
			GenerateExpression (statement.Right);
			if (dont_write_semicolon)
				return;
			output.WriteLine (';');
		}

		protected override void GenerateAttachEventStatement (CodeAttachEventStatement statement)
		{
			TextWriter output = Output;

			GenerateEventReferenceExpression (statement.Event);
			output.Write (" += ");
			GenerateExpression (statement.Listener);
			output.WriteLine (';');
		}

		protected override void GenerateRemoveEventStatement (CodeRemoveEventStatement statement)
		{
			TextWriter output = Output;
			GenerateEventReferenceExpression (statement.Event);
			output.Write (" -= ");
			GenerateExpression (statement.Listener);
			output.WriteLine (';');
		}

		protected override void GenerateGotoStatement (CodeGotoStatement statement)
		{
			TextWriter output = Output;

			output.Write ("goto ");
			output.Write (GetSafeName (statement.Label));
			output.WriteLine (";");
		}
		
		protected override void GenerateLabeledStatement (CodeLabeledStatement statement)
		{
			Indent--;
			Output.Write (statement.Label);
			Output.WriteLine (":");
			Indent++;

			if (statement.Statement != null) {
				GenerateStatement (statement.Statement);
			}
		}

		protected override void GenerateVariableDeclarationStatement (CodeVariableDeclarationStatement statement)
		{
			TextWriter output = Output;

			OutputTypeNamePair (statement.Type, GetSafeName (statement.Name));

			CodeExpression initExpression = statement.InitExpression;
			if (initExpression != null) {
				output.Write (" = ");
				GenerateExpression (initExpression);
			}

			if (!dont_write_semicolon) {
				output.WriteLine (';');
			}
		}

		protected override void GenerateLinePragmaStart (CodeLinePragma linePragma)
		{
			Output.WriteLine ();
			Output.Write ("#line ");
			Output.Write (linePragma.LineNumber);
			Output.Write (" \"");
			Output.Write (linePragma.FileName);
			Output.Write ("\"");
			Output.WriteLine ();
		}

		protected override void GenerateLinePragmaEnd (CodeLinePragma linePragma)
		{
			Output.WriteLine ();
			Output.WriteLine ("#line default");
			Output.WriteLine ("#line hidden");
		}

		protected override void GenerateEvent (CodeMemberEvent eventRef, CodeTypeDeclaration declaration)
		{
			if (IsCurrentDelegate || IsCurrentEnum) {
				return;
			}

			OutputAttributes (eventRef.CustomAttributes, null, false);

			if (eventRef.PrivateImplementationType == null) {
				OutputMemberAccessModifier (eventRef.Attributes);
			}

			Output.Write ("event ");

			if (eventRef.PrivateImplementationType != null) {
				OutputTypeNamePair (eventRef.Type,
					eventRef.PrivateImplementationType.BaseType + "." + 
					eventRef.Name);
			} else {
				OutputTypeNamePair (eventRef.Type, GetSafeName (eventRef.Name));
			}
			Output.WriteLine (';');
		}

		protected override void GenerateField (CodeMemberField field)
		{
			if (IsCurrentDelegate || IsCurrentInterface) {
				return;
			}

			TextWriter output = Output;

			OutputAttributes (field.CustomAttributes, null, false);

			if (IsCurrentEnum) {
				Output.Write (GetSafeName (field.Name));
			} else {
				MemberAttributes attributes = field.Attributes;
				OutputMemberAccessModifier (attributes);
				OutputVTableModifier (attributes);
				OutputFieldScopeModifier (attributes);

				OutputTypeNamePair (field.Type, GetSafeName (field.Name));
			}

			CodeExpression initExpression = field.InitExpression;
			if (initExpression != null) {
				output.Write (" = ");
				GenerateExpression (initExpression);
			}

			if (IsCurrentEnum)
				output.WriteLine (',');
			else
				output.WriteLine (';');
		}
		
		protected override void GenerateSnippetMember (CodeSnippetTypeMember member)
		{
			Output.Write (member.Text);
		}
		
		protected override void GenerateEntryPointMethod (CodeEntryPointMethod method, 
								  CodeTypeDeclaration declaration)
		{
			OutputAttributes (method.CustomAttributes, null, false);

			Output.Write ("public static ");
			OutputType (method.ReturnType);
			Output.Write (" Main()");
			OutputStartBrace ();
			Indent++;
			GenerateStatements (method.Statements);
			Indent--;
			Output.WriteLine ("}");
		}
		
		protected override void GenerateMethod (CodeMemberMethod method,
							CodeTypeDeclaration declaration)
		{
			if (IsCurrentDelegate || IsCurrentEnum) {
				return;
			}

			TextWriter output = Output;

			OutputAttributes (method.CustomAttributes, null, false);

			OutputAttributes (method.ReturnTypeCustomAttributes, 
				"return: ", false);

			MemberAttributes attributes = method.Attributes;

			if (!IsCurrentInterface) {
				if (method.PrivateImplementationType == null) {
					OutputMemberAccessModifier (attributes);
					OutputVTableModifier (attributes);
					OutputMemberScopeModifier (attributes);
				}
			} else {
				OutputVTableModifier (attributes);
			}

			OutputType (method.ReturnType);
			output.Write (' ');

			CodeTypeReference privateType = method.PrivateImplementationType;
			if (privateType != null) {
				output.Write (privateType.BaseType);
				output.Write ('.');
			}
			output.Write (GetSafeName (method.Name));

			GenerateGenericsParameters (method.TypeParameters);

			output.Write ('(');
			OutputParameters (method.Parameters);
			output.Write (')');

			GenerateGenericsConstraints (method.TypeParameters);

			if (IsAbstract (attributes) || declaration.IsInterface)
				output.WriteLine (';');
			else {
				OutputStartBrace ();
				++Indent;
				GenerateStatements (method.Statements);
				--Indent;
				output.WriteLine ('}');
			}
		}

		static bool IsAbstract (MemberAttributes attributes)
		{
			return (attributes & MemberAttributes.ScopeMask) == MemberAttributes.Abstract;
		}

		protected override void GenerateProperty (CodeMemberProperty property,
							  CodeTypeDeclaration declaration)
		{
			if (IsCurrentDelegate || IsCurrentEnum) {
				return;
			}

			TextWriter output = Output;

			OutputAttributes (property.CustomAttributes, null, false);

			MemberAttributes attributes = property.Attributes;

			if (!IsCurrentInterface) {
				if (property.PrivateImplementationType == null) {
					OutputMemberAccessModifier (attributes);
					OutputVTableModifier (attributes);
					OutputMemberScopeModifier (attributes);
				}
			} else {
				OutputVTableModifier (attributes);
			}

			OutputType (property.Type);
			output.Write (' ');

			if (!IsCurrentInterface && property.PrivateImplementationType != null) {
				output.Write (property.PrivateImplementationType.BaseType);
				output.Write ('.');
			}

			// only consider property indexer if name is Item (case-insensitive 
			// comparison) AND property has parameters
			if (string.Compare(property.Name, "Item", true, CultureInfo.InvariantCulture) == 0 && property.Parameters.Count > 0) {
				output.Write ("this[");
				OutputParameters(property.Parameters);
				output.Write(']');
			} else {
				output.Write (GetSafeName (property.Name));
			}
			OutputStartBrace ();
			++Indent;

			if (declaration.IsInterface || IsAbstract (property.Attributes))
			{
				if (property.HasGet) output.WriteLine("get;");
				if (property.HasSet) output.WriteLine("set;");
			}
			else
			{
				if (property.HasGet)
				{
					output.Write ("get");
					OutputStartBrace ();
					++Indent;

					GenerateStatements (property.GetStatements);

					--Indent;
					output.WriteLine ('}');
				}

				if (property.HasSet)
				{
					output.Write ("set");
					OutputStartBrace ();
					++Indent;

					GenerateStatements (property.SetStatements);

					--Indent;
					output.WriteLine ('}');
				}
			}

			--Indent;
			output.WriteLine ('}');
		}

		protected override void GenerateConstructor (CodeConstructor constructor, CodeTypeDeclaration declaration)
		{
			if (IsCurrentDelegate || IsCurrentEnum || IsCurrentInterface) {
				return;
			}

			OutputAttributes (constructor.CustomAttributes, null, false);

			OutputMemberAccessModifier (constructor.Attributes);
			Output.Write (GetSafeName (CurrentTypeName) + "(");
			OutputParameters (constructor.Parameters);
			Output.Write (")");
			if (constructor.BaseConstructorArgs.Count > 0) {
				Output.WriteLine (" : ");
				Indent += 2;
				Output.Write ("base(");
				OutputExpressionList (constructor.BaseConstructorArgs);
				Output.Write (')');
				Indent -= 2;
			}
			if (constructor.ChainedConstructorArgs.Count > 0) {
				Output.WriteLine (" : ");
				Indent += 2;
				Output.Write("this(");
				OutputExpressionList (constructor.ChainedConstructorArgs);
				Output.Write(')');
				Indent -= 2;
			}
			OutputStartBrace ();
			Indent++;
			GenerateStatements (constructor.Statements);
			Indent--;
			Output.WriteLine ('}');
		}

		protected override void GenerateTypeConstructor (CodeTypeConstructor constructor)
		{
			if (IsCurrentDelegate || IsCurrentEnum || IsCurrentInterface) {
				return;
			}

			OutputAttributes (constructor.CustomAttributes, null, false);

			Output.Write ("static " + GetSafeName (CurrentTypeName) + "()");
			OutputStartBrace ();
			Indent++;
			GenerateStatements (constructor.Statements);
			Indent--;
			Output.WriteLine ('}');
		}

		protected override void GenerateTypeStart(CodeTypeDeclaration declaration)
		{
			TextWriter output = Output;

			OutputAttributes (declaration.CustomAttributes, null, false);

			if (!IsCurrentDelegate) {
				OutputTypeAttributes (declaration);

				output.Write (GetSafeName (declaration.Name));

				GenerateGenericsParameters (declaration.TypeParameters);

				IEnumerator enumerator = declaration.BaseTypes.GetEnumerator ();
				if (enumerator.MoveNext ()) {
					CodeTypeReference type = (CodeTypeReference) enumerator.Current;

					output.Write (" : ");
					OutputType (type);

					while (enumerator.MoveNext ()) {
						type = (CodeTypeReference) enumerator.Current;

						output.Write (", ");
						OutputType (type);
					}
				}

				GenerateGenericsConstraints (declaration.TypeParameters);
				OutputStartBrace ();
				++Indent;
			} else {
				if ((declaration.TypeAttributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public) {
					output.Write ("public ");
				}

				CodeTypeDelegate delegateDecl = (CodeTypeDelegate) declaration;
				output.Write ("delegate ");
				OutputType (delegateDecl.ReturnType);
				output.Write (" ");
				output.Write (GetSafeName (declaration.Name));
				output.Write ("(");
				OutputParameters (delegateDecl.Parameters);
				output.WriteLine (");");
			}
		}

		protected override void GenerateTypeEnd (CodeTypeDeclaration declaration)
		{
			if (!IsCurrentDelegate) {
				--Indent;
				Output.WriteLine ("}");
			}
		}

		protected override void GenerateNamespaceStart (CodeNamespace ns)
		{
			TextWriter output = Output;
			
			string name = ns.Name;
			if (name != null && name.Length != 0) {
				output.Write ("namespace ");
				output.Write (GetSafeName (name));
				OutputStartBrace ();
				++Indent;
			}
		}

		protected override void GenerateNamespaceEnd (CodeNamespace ns)
		{
			string name = ns.Name;
			if (name != null && name.Length != 0) {
				--Indent;
				Output.WriteLine ("}");
			}
		}

		protected override void GenerateNamespaceImport (CodeNamespaceImport import)
		{
			TextWriter output = Output;

			output.Write ("using ");
			output.Write (GetSafeName (import.Namespace));
			output.WriteLine (';');
		}
		
		protected override void GenerateAttributeDeclarationsStart (CodeAttributeDeclarationCollection attributes)
		{
			Output.Write ('[');
		}
		
		protected override void GenerateAttributeDeclarationsEnd (CodeAttributeDeclarationCollection attributes)
		{
			Output.Write (']');
		}

		private void OutputStartBrace ()
		{
			if (Options.BracingStyle == "C") {
				Output.WriteLine ("");
				Output.WriteLine ("{");
			} else {
				Output.WriteLine (" {");
			}
		}

		private void OutputAttributes (CodeAttributeDeclarationCollection attributes, string prefix, bool inline)
		{
			bool params_set = false;

			foreach (CodeAttributeDeclaration att in attributes) {
				if (att.Name == "System.ParamArrayAttribute") {
					params_set = true;
					continue;
				}

				GenerateAttributeDeclarationsStart (attributes);
				if (prefix != null) {
					Output.Write (prefix);
				}
				OutputAttributeDeclaration (att);
				GenerateAttributeDeclarationsEnd (attributes);
				if (inline) {
					Output.Write (" ");
				} else {
					Output.WriteLine ();
				}
			}

			if (params_set) {
				if (prefix != null)
					Output.Write (prefix);
				Output.Write ("params");
				if (inline)
					Output.Write (" ");
				else
					Output.WriteLine ();
			}
		}

		private void OutputAttributeDeclaration (CodeAttributeDeclaration attribute)
		{
			Output.Write (attribute.Name.Replace ('+', '.'));
			Output.Write ('(');
			IEnumerator enumerator = attribute.Arguments.GetEnumerator ();
			if (enumerator.MoveNext ()) {
				CodeAttributeArgument argument = (CodeAttributeArgument) enumerator.Current;
				OutputAttributeArgument (argument);

				while (enumerator.MoveNext ()) {
					Output.Write (", ");
					argument = (CodeAttributeArgument) enumerator.Current;
					OutputAttributeArgument (argument);
				}
			}
			Output.Write (')');
		}

		protected override void OutputType (CodeTypeReference type)
		{
			Output.Write (GetTypeOutput (type));
		}

		private void OutputVTableModifier (MemberAttributes attributes)
		{
			if ((attributes & MemberAttributes.VTableMask) == MemberAttributes.New) {
				Output.Write ("new ");
			}
		}

		protected override void OutputFieldScopeModifier (MemberAttributes attributes)
		{
			switch (attributes & MemberAttributes.ScopeMask) {
				case MemberAttributes.Static:
					Output.Write ("static ");
					break;
				case MemberAttributes.Const:
					Output.Write ("const ");
					break;
			}
		}


		// Note: this method should in fact be private as in .NET 2.0, the 
		// CSharpCodeGenerator no longer derives from CodeGenerator but we
		// still need to make this change.
		protected override void OutputMemberAccessModifier (MemberAttributes attributes)
		{
			switch (attributes & MemberAttributes.AccessMask) {
				case MemberAttributes.Assembly:
				case MemberAttributes.FamilyAndAssembly:
					Output.Write ("internal "); 
					break;
				case MemberAttributes.Family:
					Output.Write ("protected ");
					break;
				case MemberAttributes.FamilyOrAssembly:
					Output.Write ("protected internal ");
					break;
				case MemberAttributes.Private:
					Output.Write ("private ");
					break;
				case MemberAttributes.Public:
					Output.Write ("public ");
					break;
			}
		}

		// Note: this method should in fact be private as in .NET 2.0, the 
		// CSharpCodeGenerator no longer derives from CodeGenerator but we
		// still need to make this change.
		protected override void OutputMemberScopeModifier (MemberAttributes attributes)
		{
			switch (attributes & MemberAttributes.ScopeMask) {
				case MemberAttributes.Abstract:
					Output.Write ("abstract ");
					break;
				case MemberAttributes.Final:
					// do nothing
					break;
				case MemberAttributes.Static:
					Output.Write ("static ");
					break;
				case MemberAttributes.Override:
					Output.Write ("override ");
					break;
				default:
					MemberAttributes access = attributes & MemberAttributes.AccessMask;
					if (access == MemberAttributes.Assembly || access == MemberAttributes.Family || access == MemberAttributes.Public) {
						Output.Write ("virtual ");
					}
					break;
			}
		}

		private void OutputTypeAttributes (CodeTypeDeclaration declaration)
		{
			TextWriter output = Output;
			TypeAttributes attributes = declaration.TypeAttributes;

			switch (attributes & TypeAttributes.VisibilityMask) {
				case TypeAttributes.Public:
				case TypeAttributes.NestedPublic:
					output.Write ("public ");
					break;
				case TypeAttributes.NestedPrivate:
					output.Write ("private ");
					break;
				case TypeAttributes.NotPublic:
				case TypeAttributes.NestedFamANDAssem:
				case TypeAttributes.NestedAssembly:
					output.Write ("internal ");
					break; 
				case TypeAttributes.NestedFamily:
					output.Write ("protected ");
					break;
				case TypeAttributes.NestedFamORAssem:
					output.Write ("protected internal ");
					break;
			}

			if (declaration.IsStruct) {
				if (declaration.IsPartial) {
					output.Write ("partial ");
				}
				output.Write ("struct ");
			} else if (declaration.IsEnum) {
				output.Write ("enum ");
			} else {
				if ((attributes & TypeAttributes.Interface) != 0) {
					if (declaration.IsPartial) {
						output.Write ("partial ");
					}
					output.Write ("interface ");
				} else {
					if ((attributes & TypeAttributes.Sealed) != 0)
						output.Write ("sealed ");
					if ((attributes & TypeAttributes.Abstract) != 0)
						output.Write ("abstract ");
					if (declaration.IsPartial) {
						output.Write ("partial ");
					}
					output.Write ("class ");
				}
			}
		}

		[MonoTODO ("Implement missing special characters")]
		protected override string QuoteSnippetString (string value)
		{
			// FIXME: this is weird, but works.
			string output = value.Replace ("\\", "\\\\");
			output = output.Replace ("\"", "\\\"");
			output = output.Replace ("\t", "\\t");
			output = output.Replace ("\r", "\\r");
			output = output.Replace ("\n", "\\n");

			return "\"" + output + "\"";
		}

		protected override void GeneratePrimitiveExpression(CodePrimitiveExpression e)
		{
			if (e.Value is char) {
				this.GenerateCharValue ((char) e.Value);
			} else if (e.Value is ushort) {
				ushort uc = (ushort) e.Value;
				Output.Write (uc.ToString(CultureInfo.InvariantCulture));
			} else if (e.Value is uint) {
				uint ui = (uint) e.Value;
				Output.Write (ui.ToString(CultureInfo.InvariantCulture));
				Output.Write ("u");
			} else if (e.Value is ulong) {
				ulong ul = (ulong) e.Value;
				Output.Write (ul.ToString(CultureInfo.InvariantCulture));
				Output.Write ("ul");
			} else if (e.Value is sbyte) {
				sbyte sb = (sbyte) e.Value;
				Output.Write (sb.ToString(CultureInfo.InvariantCulture));
			} else {
				base.GeneratePrimitiveExpression (e);
			}
		}

		private void GenerateCharValue (char c)
		{
			Output.Write ('\'');

			switch (c) {
				case '\0':
					Output.Write ("\\0");
					break;
				case '\t':
					Output.Write ("\\t");
					break;
				case '\n':
					Output.Write ("\\n");
					break;
				case '\r':
					Output.Write ("\\r");
					break;
				case '"':
					Output.Write ("\\\"");
					break;
				case '\'':
					Output.Write ("\\'");
					break;
				case '\\':
					Output.Write ("\\\\");
					break;
				case '\u2028':
					Output.Write ("\\u");
					Output.Write (((int) c).ToString ("X4", CultureInfo.InvariantCulture));
					break;
				case '\u2029':
					Output.Write ("\\u");
					Output.Write (((int) c).ToString ("X4", CultureInfo.InvariantCulture));
					break;
				default:
					Output.Write (c);
					break;
			}

			Output.Write ('\'');
		}

		protected override void GenerateSingleFloatValue (float f)
		{
			base.GenerateSingleFloatValue (f);
			base.Output.Write ('F');
		}

		protected override void GenerateDecimalValue (decimal d)
		{
			base.GenerateDecimalValue (d);
			base.Output.Write ('m');
		}

		protected override void GenerateParameterDeclarationExpression (CodeParameterDeclarationExpression e)
		{
			OutputAttributes (e.CustomAttributes, null, true);
			OutputDirection (e.Direction);
			OutputType (e.Type);
			Output.Write (' ');
			Output.Write (GetSafeName (e.Name));
		}

		protected override void GenerateTypeOfExpression (CodeTypeOfExpression e)
		{
			Output.Write ("typeof(");
			OutputType (e.Type);
			Output.Write (")");
		}

		/* 
		 * ICodeGenerator
		 */

		protected override string CreateEscapedIdentifier (string value)
		{
			if (value == null)
				throw new NullReferenceException ("Argument identifier is null.");
			return GetSafeName (value);
		}

		protected override string CreateValidIdentifier (string value)
		{
			if (value == null)
				throw new NullReferenceException ();

			if (keywordsTable == null)
				FillKeywordTable ();

			if (keywordsTable.Contains (value))
				return "_" + value;
			else
				return value;
		}
    
		protected override string GetTypeOutput (CodeTypeReference type)
		{
			if ((type.Options & CodeTypeReferenceOptions.GenericTypeParameter) != 0)
				return type.BaseType;

			string typeOutput = null;

			if (type.ArrayElementType != null) {
				typeOutput = GetTypeOutput (type.ArrayElementType);
			} else {
				typeOutput = DetermineTypeOutput (type);
			}

			int rank = type.ArrayRank;
			if (rank > 0) {
				typeOutput += '[';
				for (--rank; rank > 0; --rank) {
					typeOutput += ',';
				}
				typeOutput += ']';
			}

			return typeOutput;
		}

		private string DetermineTypeOutput (CodeTypeReference type)
		{
			string typeOutput = null;
			string baseType = type.BaseType;

			switch (baseType.ToLower (System.Globalization.CultureInfo.InvariantCulture)) {
				case "system.int32":
					typeOutput = "int";
					break;
				case "system.int64":
					typeOutput = "long";
					break;
				case "system.int16":
					typeOutput = "short";
					break;
				case "system.boolean":
					typeOutput = "bool";
					break;
				case "system.char":
					typeOutput = "char";
					break;
				case "system.string":
					typeOutput = "string";
					break;
				case "system.object":
					typeOutput = "object";
					break;
				case "system.void":
					typeOutput = "void";
					break;
				case "system.byte":
					typeOutput = "byte";
					break;
				case "system.sbyte":
					typeOutput = "sbyte";
					break;
				case "system.decimal":
					typeOutput = "decimal";
					break;
				case "system.double":
					typeOutput = "double";
					break;
				case "system.single":
					typeOutput = "float";
					break;
				case "system.uint16":
					typeOutput = "ushort";
					break;
				case "system.uint32":
					typeOutput = "uint";
					break;
				case "system.uint64":
					typeOutput = "ulong";
					break;
				default:
					StringBuilder sb = new StringBuilder (baseType.Length);
					if (type.Options == CodeTypeReferenceOptions.GlobalReference) {
						sb.Append ("global::");
					}

					int lastProcessedChar = 0;
					for (int i = 0; i < baseType.Length; i++) {
						char currentChar = baseType[i];
						if (currentChar != '+' && currentChar != '.') {
							if (currentChar == '`') {
								sb.Append (CreateEscapedIdentifier (baseType.Substring (
									lastProcessedChar, i - lastProcessedChar)));
								// skip ` character
								i++;
								// determine number of type arguments to output
								int end = i;
								while (end < baseType.Length && Char.IsDigit (baseType [end]))
									end++;
								int typeArgCount = Int32.Parse (baseType.Substring (i, end - i));
								// output type arguments
								OutputTypeArguments (type.TypeArguments, sb, typeArgCount);
								// skip type argument indicator
								i = end;
								// if next character is . or +, then append .
								if ((i < baseType.Length) && ((baseType[i] == '+') || (baseType[i] == '.'))) {
									sb.Append ('.');
									// skip character that we just processed
									i++;
								}
								// save postion of last processed character
								lastProcessedChar = i;
							}
						} else {
							sb.Append (CreateEscapedIdentifier (baseType.Substring (
								lastProcessedChar, i - lastProcessedChar)));
							sb.Append ('.');
							// skip separator
							i++;
							// save postion of last processed character
							lastProcessedChar = i;
						}
					}

					// add characters that have not yet been processed 
					if (lastProcessedChar < baseType.Length) {
						sb.Append (CreateEscapedIdentifier (baseType.Substring (lastProcessedChar)));
					}

					typeOutput = sb.ToString ();
					break;
			}
			return typeOutput;
		}

		static bool is_identifier_start_character (char c)
                {
                        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '@' || Char.IsLetter (c);
                }

                static bool is_identifier_part_character (char c)
                {
                        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') || Char.IsLetter (c);
                }
		
		protected override bool IsValidIdentifier (string identifier)
		{
			if (identifier == null || identifier.Length == 0)
				return false;
			
			if (keywordsTable == null)
				FillKeywordTable ();

			if (keywordsTable.Contains (identifier))
				return false;

			if (!is_identifier_start_character (identifier [0]))
                                return false;
                        
                        for (int i = 1; i < identifier.Length; i ++)
                                if (! is_identifier_part_character (identifier [i]))
                                        return false;
                        
                        return true;
		}

		protected override bool Supports (GeneratorSupport supports)
		{
			return true;
		}

		protected override void GenerateDirectives (CodeDirectiveCollection directives)
		{
			foreach (CodeDirective d in directives) {
				if (d is CodeChecksumPragma) {
					GenerateCodeChecksumPragma ((CodeChecksumPragma)d);
					continue;
				}
				if (d is CodeRegionDirective) {
					GenerateCodeRegionDirective ((CodeRegionDirective)d);
					continue;
				}
				throw new NotImplementedException ("Unknown CodeDirective");
			}
		}

		void GenerateCodeChecksumPragma (CodeChecksumPragma pragma)
		{
			Output.Write ("#pragma checksum ");
			Output.Write (QuoteSnippetString (pragma.FileName));
			Output.Write (" \"");
			Output.Write (pragma.ChecksumAlgorithmId.ToString ("B"));
			Output.Write ("\" \"");
			if (pragma.ChecksumData != null) {
				foreach (byte b in pragma.ChecksumData) {
					Output.Write (b.ToString ("X2"));
				}
			}
			Output.WriteLine ("\"");
		}

		void GenerateCodeRegionDirective (CodeRegionDirective region)
		{
			switch (region.RegionMode) {
				case CodeRegionMode.Start:
					Output.Write ("#region ");
					Output.WriteLine (region.RegionText);
					return;
				case CodeRegionMode.End:
					Output.WriteLine ("#endregion");
					return;
			}
		}

		void GenerateGenericsParameters (CodeTypeParameterCollection parameters)
		{
			int count = parameters.Count;
			if (count == 0)
				return;

			Output.Write ('<');
			for (int i = 0; i < count - 1; ++i) {
				Output.Write (parameters [i].Name);
				Output.Write (", ");
			}
			Output.Write (parameters [count - 1].Name);
			Output.Write ('>');
		}

		void GenerateGenericsConstraints (CodeTypeParameterCollection parameters)
		{
			int count = parameters.Count;
			if (count == 0)
				return;

			bool indented = false;
			
			for (int i = 0; i < count; i++) {
				CodeTypeParameter p = parameters [i];
				bool hasConstraints = (p.Constraints.Count != 0);
				Output.WriteLine ();
				if (!hasConstraints && !p.HasConstructorConstraint)
					continue;

				if (!indented) {
					++Indent;
					indented = true;
				}

				Output.Write ("where ");
				Output.Write (p.Name);
				Output.Write (" : ");

				for (int j = 0; j < p.Constraints.Count; j++) {
					if (j > 0)
						Output.Write (", ");
					OutputType (p.Constraints [j]);
				}

				if (p.HasConstructorConstraint) {
					if (hasConstraints)
						Output.Write (", ");
					Output.Write ("new");
					if (hasConstraints)
						Output.Write (" ");
					Output.Write ("()");
				}
			}

			if (indented)
				--Indent;
		}

		string GetTypeArguments (CodeTypeReferenceCollection collection)
		{
			StringBuilder sb = new StringBuilder (" <");
			foreach (CodeTypeReference r in collection) {
				sb.Append (GetTypeOutput (r));
				sb.Append (", ");
			}
			sb.Length--;
			sb [sb.Length - 1] = '>';
			return sb.ToString ();
		}

		private void OutputTypeArguments (CodeTypeReferenceCollection typeArguments, StringBuilder sb, int count)
		{
			if (count == 0) {
				return;
			} else if (typeArguments.Count == 0) {
				// generic type definition
				sb.Append ("<>");
				return;
			}

			sb.Append ('<');

			// write first type argument
			sb.Append (GetTypeOutput (typeArguments[0]));
			// subsequent type argument are prefixed by ', ' separator
			for (int i = 1; i < count; i++) {
				sb.Append (", ");
				sb.Append (GetTypeOutput (typeArguments[i]));
			}

			sb.Append ('>');
		}

#if false
		//[MonoTODO]
		public override void ValidateIdentifier (string identifier)
		{
		}
#endif

		private string GetSafeName (string id)
		{
			if (keywordsTable == null) {
				FillKeywordTable ();
			}
			if (keywordsTable.Contains (id)) {
				return "@" + id;
			} else {
				return id;
			}
		}

		static void FillKeywordTable ()
		{
			keywordsTable = new Hashtable ();
			foreach (string keyword in keywords) {
				keywordsTable.Add (keyword, keyword);
			}
		}

		private static Hashtable keywordsTable;
		private static string[] keywords = new string[] {
			"abstract","event","new","struct","as","explicit","null","switch","base","extern",
			"this","false","operator","throw","break","finally","out","true",
			"fixed","override","try","case","params","typeof","catch","for",
			"private","foreach","protected","checked","goto","public",
			"unchecked","class","if","readonly","unsafe","const","implicit","ref",
			"continue","in","return","using","virtual","default",
			"interface","sealed","volatile","delegate","internal","do","is",
			"sizeof","while","lock","stackalloc","else","static","enum",
			"namespace",
			"object","bool","byte","float","uint","char","ulong","ushort",
			"decimal","int","sbyte","short","double","long","string","void",
			"partial", "yield", "where"
		};
	}
}
